﻿// Inner Fire 游戏引擎库
// 场景分装 - 法线贴图
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-29

#include <dx_graphics.h>

namespace ifire {

void DxGraphics::Normal_Update() {
  Normal_UpdateObjectCBs();
  Normal_UpdateMaterialBuffer();
  Normal_UpdateMainPassCB();
}
void DxGraphics::Normal_InitPipeline() {
  Normal_LoadTextures();
  Normal_BuildRootSignature();
  Normal_BuildDescriptorHeaps();
  Normal_BuildShadersAndInputLayout();
  Normal_BuildShapeGeometry();
  Normal_BuildMaterials();
  Normal_BuildRenderItems();
  Normal_BuildFrameResources();
  Normal_BuildPSOs();
}
void DxGraphics::Normal_Draw() {
  auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;

  // Reuse the memory associated with command recording.
  // We can only reset when the associated command lists have finished execution
  // on the GPU.
  ThrowIfFailed(cmdListAlloc->Reset());

  // A command list can be reset after it has been added to the command queue
  // via ExecuteCommandList. Reusing the command list reuses memory.
  ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));

  mCommandList->RSSetViewports(1, &mScreenViewport);
  mCommandList->RSSetScissorRects(1, &mScissorRect);

  // Indicate a state transition on the resource usage.
  auto to_render = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  mCommandList->ResourceBarrier(1, &to_render);

  // Clear the back buffer and depth buffer.
  auto dsv = DepthStencilView();
  auto back = CurrentBackBufferView();
  mCommandList->ClearRenderTargetView(back, Colors::LightSteelBlue, 0, nullptr);
  mCommandList->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Specify the buffers we are going to render to.
  mCommandList->OMSetRenderTargets(1, &back, true, &dsv);

  ID3D12DescriptorHeap* descriptorHeaps[] = {mSrvDescriptorHeap.Get()};
  mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

  mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

  auto passCB = mCurrFrameResource->PassCB->Resource();
  mCommandList->SetGraphicsRootConstantBufferView(
      1, passCB->GetGPUVirtualAddress());

  // Bind all the materials used in this scene.  For structured buffers, we can
  // bypass the heap and set as a root descriptor.
  auto matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
  mCommandList->SetGraphicsRootShaderResourceView(
      2, matBuffer->GetGPUVirtualAddress());

  // Bind the sky cube map.  For our demos, we just use one "world" cube map
  // representing the environment from far away, so all objects will use the
  // same cube map and we only need to set it once per-frame. If we wanted to
  // use "local" cube maps, we would have to change them per-object, or
  // dynamically index into an array of cube maps.

  CD3DX12_GPU_DESCRIPTOR_HANDLE skyTexDescriptor(
      mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  skyTexDescriptor.Offset(mSkyTexHeapIndex, mCbvSrvDescriptorSize);
  mCommandList->SetGraphicsRootDescriptorTable(3, skyTexDescriptor);

  // Bind all the textures used in this scene.  Observe
  // that we only have to specify the first descriptor in the table.
  // The root signature knows how many descriptors are expected in the table.
  mCommandList->SetGraphicsRootDescriptorTable(
      4, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());

  Normal_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

  mCommandList->SetPipelineState(mPSOs["sky"].Get());
  Normal_DrawRenderItems(
      mCommandList.Get(), mRitemLayer[(int)RenderLayer::Sky]);

  // Indicate a state transition on the resource usage.
  auto to_present = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
  mCommandList->ResourceBarrier(1, &to_present);

  // Done recording commands.
  ThrowIfFailed(mCommandList->Close());

  // Add the command list to the queue for execution.
  ID3D12CommandList* cmdsLists[] = {mCommandList.Get()};
  mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

  // Swap the back and front buffers
  ThrowIfFailed(mSwapChain->Present(0, 0));
  mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

  // Advance the fence value to mark commands up to this fence point.
  mCurrFrameResource->Fence = ++mCurrentFence;

  // Add an instruction to the command queue to set a new fence point.
  // Because we are on the GPU timeline, the new fence point won't be
  // set until the GPU finishes processing all the commands prior to this
  // Signal().
  mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
void DxGraphics::Normal_DrawRenderItems(ID3D12GraphicsCommandList* cmdList,
    const std::vector<RenderItem*>& ritems) {
  DrawRenderItems_V2(cmdList, ritems);
}
void DxGraphics::Normal_LoadTextures() {
  std::vector<std::string> texNames = {"bricksDiffuseMap", "bricksNormalMap",
      "tileDiffuseMap", "tileNormalMap", "defaultDiffuseMap",
      "defaultNormalMap", "skyCubeMap"};

  std::vector<std::string> texFilenames = {"Textures/bricks2.dds",
      "Textures/bricks2_nmap.dds", "Textures/tile.dds",
      "Textures/tile_nmap.dds", "Textures/white1x1.dds",
      "Textures/default_nmap.dds", "Textures/snowcube1024.dds"};

  for (int i = 0; i < (int)texNames.size(); ++i) {
    auto texMap = std::make_unique<Texture>();
    texMap->Name = texNames[i];
    texMap->Filename = texFilenames[i];
    ThrowIfFailed(LoadDDS(
        texMap->Filename.c_str(), texMap->Resource, texMap->UploadHeap));

    mTextures[texMap->Name] = std::move(texMap);
  }
}
void DxGraphics::Normal_BuildRootSignature() {
  CD3DX12_DESCRIPTOR_RANGE texTable0;
  texTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0);

  CD3DX12_DESCRIPTOR_RANGE texTable1;
  texTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 10, 1, 0);

  // Root parameter can be a table, root descriptor or root constants.
  CD3DX12_ROOT_PARAMETER slotRootParameter[5];

  // Perfomance TIP: Order from most frequent to least frequent.
  slotRootParameter[0].InitAsConstantBufferView(0);
  slotRootParameter[1].InitAsConstantBufferView(1);
  slotRootParameter[2].InitAsShaderResourceView(0, 1);
  slotRootParameter[3].InitAsDescriptorTable(
      1, &texTable0, D3D12_SHADER_VISIBILITY_PIXEL);
  slotRootParameter[4].InitAsDescriptorTable(
      1, &texTable1, D3D12_SHADER_VISIBILITY_PIXEL);

  auto staticSamplers = GetStaticSamplers();

  // A root signature is an array of root parameters.
  CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(5, slotRootParameter,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // create a root signature with a single slot which points to a descriptor
  // range consisting of a single constant buffer
  ComPtr<ID3DBlob> serializedRootSig = nullptr;
  ComPtr<ID3DBlob> errorBlob = nullptr;
  HRESULT hr =
      D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
          serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

  if (errorBlob != nullptr) {
    ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
  }
  ThrowIfFailed(hr);

  ThrowIfFailed(md3dDevice->CreateRootSignature(0,
      serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
      IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
void DxGraphics::Normal_BuildDescriptorHeaps() {
  //
  // Create the SRV heap.
  //
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = 10;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));

  //
  // Fill out the heap with actual descriptors.
  //
  CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
      mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

  std::vector<ComPtr<ID3D12Resource>> tex2DList = {
      mTextures["bricksDiffuseMap"]->Resource,
      mTextures["bricksNormalMap"]->Resource,
      mTextures["tileDiffuseMap"]->Resource,
      mTextures["tileNormalMap"]->Resource,
      mTextures["defaultDiffuseMap"]->Resource,
      mTextures["defaultNormalMap"]->Resource};

  auto skyCubeMap = mTextures["skyCubeMap"]->Resource;

  D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;

  for (UINT i = 0; i < (UINT)tex2DList.size(); ++i) {
    srvDesc.Format = tex2DList[i]->GetDesc().Format;
    srvDesc.Texture2D.MipLevels = tex2DList[i]->GetDesc().MipLevels;
    md3dDevice->CreateShaderResourceView(
        tex2DList[i].Get(), &srvDesc, hDescriptor);

    // next descriptor
    hDescriptor.Offset(1, mCbvSrvDescriptorSize);
  }

  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
  srvDesc.TextureCube.MostDetailedMip = 0;
  srvDesc.TextureCube.MipLevels = skyCubeMap->GetDesc().MipLevels;
  srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
  srvDesc.Format = skyCubeMap->GetDesc().Format;
  md3dDevice->CreateShaderResourceView(skyCubeMap.Get(), &srvDesc, hDescriptor);

  mSkyTexHeapIndex = (UINT)tex2DList.size();
}
void DxGraphics::Normal_BuildShadersAndInputLayout() {
  const D3D_SHADER_MACRO alphaTestDefines[] = {"ALPHA_TEST", "1", NULL, NULL};

  mShaders["standardVS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Normal.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["opaquePS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Normal.hlsl", nullptr, "PS", "ps_5_1");

  mShaders["skyVS"] = d3dUtil::CompileShader(
      "Shaders\\Sky_Normal.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["skyPS"] = d3dUtil::CompileShader(
      "Shaders\\Sky_Normal.hlsl", nullptr, "PS", "ps_5_1");

  mInputLayout = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
  };
}
void DxGraphics::Normal_BuildShapeGeometry() {

  GeometryGenerator geoGen;
  GeometryGenerator::MeshData box = geoGen.CreateBox(1.0f, 1.0f, 1.0f, 3);
  GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
  GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
  GeometryGenerator::MeshData cylinder =
      geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);

  //
  // We are concatenating all the geometry into one big vertex/index buffer.  So
  // define the regions in the buffer each submesh covers.
  //

  // Cache the vertex offsets to each object in the concatenated vertex buffer.
  UINT boxVertexOffset = 0;
  UINT gridVertexOffset = (UINT)box.Vertices.size();
  UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
  UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();

  // Cache the starting index for each object in the concatenated index buffer.
  UINT boxIndexOffset = 0;
  UINT gridIndexOffset = (UINT)box.Indices32.size();
  UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
  UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();

  SubmeshGeometry boxSubmesh;
  boxSubmesh.IndexCount = (UINT)box.Indices32.size();
  boxSubmesh.StartIndexLocation = boxIndexOffset;
  boxSubmesh.BaseVertexLocation = boxVertexOffset;

  SubmeshGeometry gridSubmesh;
  gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
  gridSubmesh.StartIndexLocation = gridIndexOffset;
  gridSubmesh.BaseVertexLocation = gridVertexOffset;

  SubmeshGeometry sphereSubmesh;
  sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
  sphereSubmesh.StartIndexLocation = sphereIndexOffset;
  sphereSubmesh.BaseVertexLocation = sphereVertexOffset;

  SubmeshGeometry cylinderSubmesh;
  cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
  cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
  cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;

  //
  // Extract the vertex elements we are interested in and pack the
  // vertices of all the meshes into one vertex buffer.
  //

  auto totalVertexCount = box.Vertices.size() + grid.Vertices.size() +
                          sphere.Vertices.size() + cylinder.Vertices.size();

  std::vector<VertexV2> vertices(totalVertexCount);

  UINT k = 0;
  for (size_t i = 0; i < box.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = box.Vertices[i].Position;
    vertices[k].Normal = box.Vertices[i].Normal;
    vertices[k].TexC = box.Vertices[i].TexC;
    vertices[k].TangentU = box.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < grid.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = grid.Vertices[i].Position;
    vertices[k].Normal = grid.Vertices[i].Normal;
    vertices[k].TexC = grid.Vertices[i].TexC;
    vertices[k].TangentU = grid.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < sphere.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = sphere.Vertices[i].Position;
    vertices[k].Normal = sphere.Vertices[i].Normal;
    vertices[k].TexC = sphere.Vertices[i].TexC;
    vertices[k].TangentU = sphere.Vertices[i].TangentU;
  }

  for (size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = cylinder.Vertices[i].Position;
    vertices[k].Normal = cylinder.Vertices[i].Normal;
    vertices[k].TexC = cylinder.Vertices[i].TexC;
    vertices[k].TangentU = cylinder.Vertices[i].TangentU;
  }

  std::vector<std::uint16_t> indices;
  indices.insert(indices.end(), std::begin(box.GetIndices16()),
      std::end(box.GetIndices16()));
  indices.insert(indices.end(), std::begin(grid.GetIndices16()),
      std::end(grid.GetIndices16()));
  indices.insert(indices.end(), std::begin(sphere.GetIndices16()),
      std::end(sphere.GetIndices16()));
  indices.insert(indices.end(), std::begin(cylinder.GetIndices16()),
      std::end(cylinder.GetIndices16()));

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(VertexV2);
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "shapeGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(VertexV2);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  geo->DrawArgs["box"] = boxSubmesh;
  geo->DrawArgs["grid"] = gridSubmesh;
  geo->DrawArgs["sphere"] = sphereSubmesh;
  geo->DrawArgs["cylinder"] = cylinderSubmesh;

  mGeometries[geo->Name] = std::move(geo);
}
void DxGraphics::Normal_BuildMaterials() {
  auto bricks0 = std::make_unique<Material>();
  bricks0->Name = "bricks0";
  bricks0->MatCBIndex = 0;
  bricks0->DiffuseSrvHeapIndex = 0;
  bricks0->NormalSrvHeapIndex = 1;
  bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks0->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  bricks0->Roughness = 0.3f;

  auto tile0 = std::make_unique<Material>();
  tile0->Name = "tile0";
  tile0->MatCBIndex = 2;
  tile0->DiffuseSrvHeapIndex = 2;
  tile0->NormalSrvHeapIndex = 3;
  tile0->DiffuseAlbedo = XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f);
  tile0->FresnelR0 = XMFLOAT3(0.2f, 0.2f, 0.2f);
  tile0->Roughness = 0.1f;

  auto mirror0 = std::make_unique<Material>();
  mirror0->Name = "mirror0";
  mirror0->MatCBIndex = 3;
  mirror0->DiffuseSrvHeapIndex = 4;
  mirror0->NormalSrvHeapIndex = 5;
  mirror0->DiffuseAlbedo = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
  mirror0->FresnelR0 = XMFLOAT3(0.98f, 0.97f, 0.95f);
  mirror0->Roughness = 0.1f;

  auto sky = std::make_unique<Material>();
  sky->Name = "sky";
  sky->MatCBIndex = 4;
  sky->DiffuseSrvHeapIndex = 6;
  sky->NormalSrvHeapIndex = 7;
  sky->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  sky->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  sky->Roughness = 1.0f;

  mMaterials["bricks0"] = std::move(bricks0);
  mMaterials["tile0"] = std::move(tile0);
  mMaterials["mirror0"] = std::move(mirror0);
  mMaterials["sky"] = std::move(sky);
}
void DxGraphics::Normal_BuildRenderItems() {
  auto skyRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&skyRitem->World, XMMatrixScaling(5000.0f, 5000.0f, 5000.0f));
  skyRitem->TexTransform = MathHelper::Identity4x4();
  skyRitem->ObjCBIndex = 0;
  skyRitem->Mat = mMaterials["sky"].get();
  skyRitem->Geo = mGeometries["shapeGeo"].get();
  skyRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skyRitem->IndexCount = skyRitem->Geo->DrawArgs["sphere"].IndexCount;
  skyRitem->StartIndexLocation =
      skyRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
  skyRitem->BaseVertexLocation =
      skyRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Sky].push_back(skyRitem.get());
  mAllRitems.push_back(std::move(skyRitem));

  auto boxRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 1.0f, 2.0f) *
                                        XMMatrixTranslation(0.0f, 0.5f, 0.0f));
  XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 0.5f, 1.0f));
  boxRitem->ObjCBIndex = 1;
  boxRitem->Mat = mMaterials["bricks0"].get();
  boxRitem->Geo = mGeometries["shapeGeo"].get();
  boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
  boxRitem->StartIndexLocation =
      boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
  boxRitem->BaseVertexLocation =
      boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(boxRitem.get());
  mAllRitems.push_back(std::move(boxRitem));

  auto globeRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(
      &globeRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f) *
                              XMMatrixTranslation(0.0f, 2.0f, 0.0f));
  XMStoreFloat4x4(&globeRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
  globeRitem->ObjCBIndex = 2;
  globeRitem->Mat = mMaterials["mirror0"].get();
  globeRitem->Geo = mGeometries["shapeGeo"].get();
  globeRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  globeRitem->IndexCount = globeRitem->Geo->DrawArgs["sphere"].IndexCount;
  globeRitem->StartIndexLocation =
      globeRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
  globeRitem->BaseVertexLocation =
      globeRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(globeRitem.get());
  mAllRitems.push_back(std::move(globeRitem));

  auto gridRitem = std::make_unique<RenderItem>();
  gridRitem->World = MathHelper::Identity4x4();
  XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
  gridRitem->ObjCBIndex = 3;
  gridRitem->Mat = mMaterials["tile0"].get();
  gridRitem->Geo = mGeometries["shapeGeo"].get();
  gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  gridRitem->StartIndexLocation =
      gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  gridRitem->BaseVertexLocation =
      gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
  mAllRitems.push_back(std::move(gridRitem));

  XMMATRIX brickTexTransform = XMMatrixScaling(1.5f, 2.0f, 1.0f);
  UINT objCBIndex = 4;
  for (int i = 0; i < 5; ++i) {
    auto leftCylRitem = std::make_unique<RenderItem>();
    auto rightCylRitem = std::make_unique<RenderItem>();
    auto leftSphereRitem = std::make_unique<RenderItem>();
    auto rightSphereRitem = std::make_unique<RenderItem>();

    XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
    XMMATRIX rightCylWorld =
        XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);

    XMMATRIX leftSphereWorld =
        XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
    XMMATRIX rightSphereWorld =
        XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);

    XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
    XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
    leftCylRitem->ObjCBIndex = objCBIndex++;
    leftCylRitem->Mat = mMaterials["bricks0"].get();
    leftCylRitem->Geo = mGeometries["shapeGeo"].get();
    leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftCylRitem->IndexCount =
        leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    leftCylRitem->StartIndexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    leftCylRitem->BaseVertexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
    XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
    rightCylRitem->ObjCBIndex = objCBIndex++;
    rightCylRitem->Mat = mMaterials["bricks0"].get();
    rightCylRitem->Geo = mGeometries["shapeGeo"].get();
    rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightCylRitem->IndexCount =
        rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    rightCylRitem->StartIndexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    rightCylRitem->BaseVertexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
    leftSphereRitem->TexTransform = MathHelper::Identity4x4();
    leftSphereRitem->ObjCBIndex = objCBIndex++;
    leftSphereRitem->Mat = mMaterials["mirror0"].get();
    leftSphereRitem->Geo = mGeometries["shapeGeo"].get();
    leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftSphereRitem->IndexCount =
        leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    leftSphereRitem->StartIndexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    leftSphereRitem->BaseVertexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
    rightSphereRitem->TexTransform = MathHelper::Identity4x4();
    rightSphereRitem->ObjCBIndex = objCBIndex++;
    rightSphereRitem->Mat = mMaterials["mirror0"].get();
    rightSphereRitem->Geo = mGeometries["shapeGeo"].get();
    rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightSphereRitem->IndexCount =
        rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    rightSphereRitem->StartIndexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    rightSphereRitem->BaseVertexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftSphereRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightSphereRitem.get());

    mAllRitems.push_back(std::move(leftCylRitem));
    mAllRitems.push_back(std::move(rightCylRitem));
    mAllRitems.push_back(std::move(leftSphereRitem));
    mAllRitems.push_back(std::move(rightSphereRitem));
  }
}
void DxGraphics::Normal_BuildFrameResources() { BuildFrameResources_V2(); }
void DxGraphics::Normal_BuildPSOs() {
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

  //
  // PSO for opaque objects.
  //
  ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  opaquePsoDesc.InputLayout = {mInputLayout.data(), (UINT)mInputLayout.size()};
  opaquePsoDesc.pRootSignature = mRootSignature.Get();
  opaquePsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
      mShaders["standardVS"]->GetBufferSize()};
  opaquePsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
      mShaders["opaquePS"]->GetBufferSize()};
  opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaquePsoDesc.SampleMask = UINT_MAX;
  opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaquePsoDesc.NumRenderTargets = 1;
  opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
  opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  opaquePsoDesc.DSVFormat = mDepthStencilFormat;
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));

  //
  // PSO for sky.
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC skyPsoDesc = opaquePsoDesc;

  // The camera is inside the sky sphere, so just turn off culling.
  skyPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;

  // Make sure the depth function is LESS_EQUAL and not just LESS.
  // Otherwise, the normalized depth values at z = 1 (NDC) will
  // fail the depth test if the depth buffer was cleared to 1.
  skyPsoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
  skyPsoDesc.pRootSignature = mRootSignature.Get();
  skyPsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["skyVS"]->GetBufferPointer()),
      mShaders["skyVS"]->GetBufferSize()};
  skyPsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["skyPS"]->GetBufferPointer()),
      mShaders["skyPS"]->GetBufferSize()};
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &skyPsoDesc, IID_PPV_ARGS(&mPSOs["sky"])));
}
void DxGraphics::Normal_UpdateObjectCBs() { UpdateObjectCBs_V2(); }
void DxGraphics::Normal_UpdateMaterialBuffer() {
  auto currMaterialBuffer = mCurrFrameResource->MaterialBuffer.get();
  for (auto& e : mMaterials) {
    // Only update the cbuffer data if the constants have changed.  If the
    // cbuffer data changes, it needs to be updated for each FrameResource.
    Material* mat = e.second.get();
    if (mat->NumFramesDirty > 0) {
      XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

      MaterialData matData;
      matData.DiffuseAlbedo = mat->DiffuseAlbedo;
      matData.FresnelR0 = mat->FresnelR0;
      matData.Roughness = mat->Roughness;
      XMStoreFloat4x4(&matData.MatTransform, XMMatrixTranspose(matTransform));
      matData.DiffuseMapIndex = mat->DiffuseSrvHeapIndex;
      matData.NormalMapIndex = mat->NormalSrvHeapIndex;

      currMaterialBuffer->CopyData(mat->MatCBIndex, matData);

      // Next FrameResource need to be updated too.
      mat->NumFramesDirty--;
    }
  }
}
void DxGraphics::Normal_UpdateMainPassCB() { UpdateMainPassCB_V2(); }


} // namespace ifire